const isMock = "true"
class RealPersonRepo {
  getList() {
    return [{
      id: 1,
      name: "张三"
    }]
  }
  getItem() {
    return {
      id: 1,
      name: "张三"
    }
  }
}
const loadAllPerson = () => {
  if (isMock) {
    return [{
      id: 1,
      name: "张三（模拟）"
    }]
  } else {
    return new RealPersonRepo().getList()
  }
}
// -----------------
class MockPersonRepo extends AbstractPersonRepo {
  getList() {
    return [{
      id: 1,
      name: "张三（模拟）"
    }]
  }
  getItem() {
    return {
      id: 1,
      name: "张三（模拟）"
    }
  }
}
const loadAllPerson = () => {
  if (isMock) {
    return new MockPersonRepo().getList()
  } else {
    return new RealPersonRepo().getList()
  }
}
// -----------------
const createPersonRepo = function () {
  if (isMock) {
    return new MockPersonRepo()
  } else {
    return new RealPersonRepo()
  }
}
const loadAllPerson = (repo) => {
  return repo.getList()
}
loadAllPerson(createPersonRepo())
// -----------------

class AbstractPersonRepo {
  getList() {
    throw new Exception("抽象方法不能直接调用")
  }
  getItem() {
    throw new Exception("抽象方法不能直接调用")
  }
}

class RealPersonRepo extends AbstractPersonRepo {
  getList() {
    return [{
      id: 1,
      name: "张三"
    }]
  }
  getItem() {
    return {
      id: 1,
      name: "张三"
    }
  }
}

class MockPersonRepo extends AbstractPersonRepo {
  getList() {
    return [{
      id: 1,
      name: "张三（模拟）"
    }]
  }
  getItem() {
    return {
      id: 1,
      name: "张三（模拟）"
    }
  }
}

const loadAllPerson = (repo) => {
  return repo.getList()
}

const allPerson = loadAllPerson(new MockPersonRepo())

// ------------------------------
class RepoFactory {
  createPersonRepo() {}
}

class RealRepoFactory extends RepoFactory {
  createPersonRepo() {
    return new RealPersonRepo()
  }
}

class MockRepoFactory extends RepoFactory {
  createPersonRepo() {
    return new MockPersonRepo()
  }
}

const CONFIG = {
  factoryClass: MockRepoFactory
}

// const factory = new MockRepoFactory()
const factory = Reflect.construct(CONFIG.factoryClass, [])
const allPerson = loadAllPerson(factory.createPersonRepo())

// -----------------------------

let personRepo = {}
personRepo.realPersonRepo = {
  getList() {
    return [{
      id: 1,
      name: "张三"
    }]
  },
  getItem() {
    return {
      id: 1,
      name: "张三"
    }
  }
}
personRepo.mockPersonRepo = {
  getList() {
    return [{
      id: 1,
      name: "张三（模拟）"
    }]
  },
  getItem() {
    return {
      id: 1,
      name: "张三（模拟）"
    }
  }
}

const CONFIG = {
  factoryName: 'mock'
}


createPersonRepo() {
  return personRepo[`${CONFIG.factoryName}PersonRepo`]
}

const loadAllPerson = (repo) => {
  return repo.getList()
}
const allPerson = loadAllPerson(createPersonRepo())


//------------------------------------------
function Singleton() {
  if (Singleton.instance) {
    return Singleton.instance
  }
  Singleton.instance = this
  return this
}

const instance = {

}



// ----------------------------------
class Builder {
  constructor() {
    this.house = {
      doors: [],
      walls: []
    }
  }
  buildDoor() {
    throw new Exception("抽象方法不能直接调用")
  }
  buildWall() {
    throw new Exception("抽象方法不能直接调用")
  }
  getHouse() {
    return this.house
  }
}

class ChineseBuilder extends Builder {
  buildDoor() {
    this.house.doors.push("中式门")
  }
  buildWall() {
    this.house.walls.push("中式墙")
  }
}

class EnglishBuilder extends Builder {
  buildDoor() {
    this.house.doors.push("英式门")
  }
  buildWall() {
    this.house.walls.push("英式墙")
  }
}

class BuilderManager {
  constructor(builder) {
    this.builder = builder
    console.log(builder)
  }

  createHouse() {
    this.builder.buildDoor();
    this.builder.buildDoor();
    this.builder.buildWall();
    this.builder.buildWall();
    return this.builder.getHouse();
  }
}
const CONFIG = {
  builderClass: EnglishBuilder
}
const builder = new BuilderManager(Reflect.construct(CONFIG.builderClass, []))
const house = builder.createHouse()




const CONFIG = {
  builderName: 'chinese'
}

const builder = {
  baseBuilder: {
    house: {
      doors: [],
      walls: []
    },
    getHouse() {
      return this.house
    }
  }
}
builder.chineseBuilder = Object.create(builder.baseBuilder, {
  buildDoor: {
    value: function () {
      this.house.doors.push("中式门")
    }
  },
  buildWall: {
    value: function () {
      this.house.walls.push("中式墙")
    }
  },
})

builder.englishBuilder = Object.create(builder.baseBuilder, {
  buildDoor: {
    value: function () {
      this.house.doors.push("英式门")
    }
  },
  buildWall: {
    value: function () {
      this.house.walls.push("英式墙")
    }
  },
})

const builderManager = {
  builder: builder[`${CONFIG.builderName}Builder`],
  createHouse: function () {
    console.log(this.builder)
    this.builder.buildDoor();
    this.builder.buildDoor();
    this.builder.buildWall();
    this.builder.buildWall();
    return this.builder.getHouse();
  },
}
const house = builderManager.createHouse()


// ----------------------------------
class AbstractActor {
  constructor() {
    this.name = ""
  }
  clone() {
    return Object.create(this)
  }
  run() {
    throw new Exception("抽象方法不能直接调用")
  }
}
class NormalActor extends AbstractActor {
  run() {
    console.log(this.name + '跑')
  }
}
class FlyActor extends AbstractActor {
  run() {
    console.log(this.name + '飞')
  }
}

class GameManager {
  run(actor) {
    const actor1 = actor.clone();
    actor1.name = "角色1"
    const actor2 = actor.clone();
    actor2.name = "角色2"
    const actor3 = actor.clone();
    actor3.name = "角色3"
    actor1.run()
    actor2.run()
    actor3.run()
  }
}

new GameManager().run(new FlyActor())



const normalActor = {
  name: '无名角色',
  run: function () {
    console.log(this.name + '跑')
  }
}
const flyActor = {
  name: '无名角色',
  run: function () {
    console.log(this.name + '飞')
  }
}

const run = (actor) => {
  const actor1 = Object.create(actor);
  actor1.name = "角色1"
  actor1.run()
  const actor2 = Object.create(actor);
  actor2.name = "角色2"
  actor2.run()
  const actor3 = Object.create(actor);
  actor3.name = "角色3"
  actor3.run()
}

run(normalActor)


// 原型模式 -------------

// ======= 结构型 =======

// 适配器 ---------------
class Stack {
  constructor() {
    this.array = []
  }

  push(item) {
    this.array.push(item)
  }

  pop() {
    return this.array.pop()
  }

  peek() {
    return this.array[this.array.length - 1]
  }
}

var s = new Stack()
s.push(1);
s.push(2);
console.log(s.pop(), s)
console.log(s.peek())

// 装饰 -----------------
class Request {
  ajax() {
    return new Promise((resolve, reject) => {
      console.log("进行ajax请求")
      resolve()
    });
  }
}

class RequestWithLog {
  constructor() {
    this.request = new Request();
  }

  ajax() {
    console.log("开始ajax请求")
    return this.request.ajax().then(() => {
      console.log("结束ajax请求")
    })
  }
}

var r = new RequestWithLog();
r.ajax().then(() => {});

// 外观 -----------------
class Request {
  ajax() {
    return new Promise((resolve, reject) => {
      console.log("进行ajax请求")
      reject("网络错误")
    });
  }
}

class Logger {
  log(text) {
    console.log(text)
  }
}

class Timer {
  constructor() {
    this.action = null;
    this.invokeTimes = 0;
    this.times = 0;
    this.timeout = 1000;
    this.promise = new Promise((resolve, reject) => {
      this.resolve = resolve
      this.reject = reject
    })
    this.logger = new Logger();
  }

  retry(action, times = 3, timeout = 1000) {
    this.action = action;
    this.times = times;
    this.timeout = timeout;
    this.innerInvoke();
    return this.promise;
  }

  innerInvoke() {
    this.action().then(() => {
      this.resolve()
    }, () => {
      this.invokeTimes++;
      this.logger.log("重试第" + this.invokeTimes + "次")
      if (this.invokeTimes > this.times) {
        this.reject()
        return;
      }
      setTimeout(() => {
        this.innerInvoke()
      }, this.timeout)
    })
  }
}

class RequestFacade {
  ajax() {
    var request = new Request();
    var logger = new Logger();
    new Timer().retry(request.ajax).then(() => {
      logger.log("调用成功")
    }, () => {
      logger.log("调用失败")
    })
  }
}

new RequestFacade().ajax()

// 代理 -----------------
// 服务端代码
public class OrderService {
  public Order getOrderDetail(string id) {
    return new Order();
  }
}
// 客户端JS代码
class OrderServiceProxy {
  async getOrderDetail(id) {
    return Promise.resolve("模拟从服务端获得的数据")
  }
}
var service = new OrderServiceProxy()
var result = await service.getOrderDetail("111")

// ======= 行为型 ========

// 模板方法模式 -----------
class Page {
  load() {
    this.preload();
    this.loading();
    this.loaded();
  }
  preload() {
    throw new Exception("子类必须实现该方法")
  }
  loading() {
    throw new Exception("子类必须实现该方法")
  }
  loaded() {
    throw new Exception("子类必须实现该方法")
  }
}

class OrderListPage extends Page {
  preload() {
    console.log("OrderListPage 预加载开始")
  }
  loading() {
    console.log("OrderListPage 加载中")
  }
  loaded() {
    console.log("OrderListPage 加载完成")
  }
}

new OrderListPage().load()

// 观察者模式 -------------
// 标准的观察者模式
var subject = (function () {

  var observerList = [];

  function attach(observer) {
    if ('update' in observer) {
      observerList.push(observer)
    } else {
      throw new Exception("观察者必须拥有update方法")
    }
  }

  function detach(observer) {
    var index = observerList.indexOf(observer)
    if (index > -1) {
      observerList.splice(index, 1)
    }
  }

  function notify(event) {
    observerList.forEach(o => {
      o.update(event)
    })
  }

  return {
    attach,
    detach,
    notify
  }
})()

var observer1 = {
  update(e) {
    console.log("observer1获得通知" + e)
  }
}

var observer2 = {
  update(e) {
    console.log("observer2获得通知" + e)
  }
}
subject.attach(observer1)
subject.attach(observer2)
subject.notify("老板来了")

// 发布订阅（事件总线）----------
var eventEmitter = (function () {

  var observerList = {};

  function on(eventName, observer) {
    if (eventName in observerList) {
      observerList[eventName].push(observer)
    } else {
      observerList[eventName] = [observer]
    }
  }

  function off(eventName, observer) {
    if (eventName in observerList) {
      var index = observerList[eventName].indexOf(observer)
      if (index > -1) {
        observerList[eventName].splice(index, 1)
      }
    }
  }

  function trigger(eventName, eventData) {
    if (eventName in observerList) {
      observerList[eventName].forEach(o => {
        o(eventData)
      })
    }
  }

  return {
    on,
    off,
    trigger
  }
})()
// 用法1
eventEmitter.on("事件1", (e) => {
  console.log("触发事件1：" + e)
})
eventEmitter.on("事件2", (e) => {
  console.log("触发事件2：" + e)
})
eventEmitter.trigger("事件1", "事件1的数据")
eventEmitter.trigger("事件2", "事件2的数据")
// 用法2
var secretary = Object.create(eventEmitter)
secretary.on("老板来了", (e) => {
  console.log("看NBA的得到老板来了的通知：" + e)
})
secretary.on("老板来了", (e) => {
  console.log("看抖音的得到老板来了的通知：" + e)
})
secretary.on("老板走了", (e) => {
  console.log("看NBA的得到老板走了的通知：" + e)
})
secretary.on("老板走了", (e) => {
  console.log("看抖音的得到老板走了的通知：" + e)
})
secretary.trigger("老板来了", "已经走到前台了")
secretary.trigger("老板走了", "这周都不来了")

// 策略模式 ---------------
var isPositiveInteger = {
  validate: function (value) {
    return !!value.toString().match(/^[0-9]*[1-9][0-9]*$/i)
  }
}

var isInRange = {
  validate: function (value) {
    return isPositiveInteger.validate(value) && value < 100
  }
}

class Form {
  constructor(age, validator) {
    this.age = age;
    this.validator = validator;
    this.isValidate = this.isValidate();
  }
  setAge(age) {
    this.age = age
  }
  setValidator(validator) {
    this.validator = validator
  }
  isValidate() {
    return this.validator.validate(this.age)
  }
}
var form1 = new Form(101, isInRange)
var form2 = new Form(99, isInRange)
var form3 = new Form(101, isPositiveInteger)
var form4 = new Form(99, isPositiveInteger)
var form5 = new Form(1.1, isInRange)
console.log(form1, form2, form3, form4, form5)


// 桥接模式 -------------
// 未采用设计模式的代码
let book1 = {
  category: '图书',
  name: '西游记',
  oriPrice: 13,
}
let book2 = {
  category: '图书',
  name: '红楼梦',
  oriPrice: 12
}
let mobile1 = {
  category: '手机',
  name: '小米6',
  oriPrice: 1500
}
let mobile2 = {
  category: '手机',
  name: 'iPhone8',
  oriPrice: 6000
}

// 打折策略
function onSaleStrategy(price) {
  return price * 0.8
}

// 返现策略
function returnCashStrategy(price) {
  return price >= 100 ? (price - 50) : price
}

// 获得商品促销后总价
function getTotalPrice(goodsArr) {
  let totalPrice = 0
  goodsArr.forEach(g => {
    if (g.category === '手机') {
      // 先打折再返现
      totalPrice += returnCashStrategy(onSaleStrategy(g.oriPrice))
    } else if (g.category === '图书') {
      // 打折
      totalPrice += onSaleStrategy(g.oriPrice)
    } else {
      totalPrice += g.oriPrice
    }
  })
  return totalPrice
}

getTotalPrice([book1, mobile1])

// 采用桥接模式的代码
class Goods {
  constructor(category, name, oriPrice, promotions) {
    this.category = category
    this.name = name
    this.oriPrice = oriPrice
    this.promotions = promotions
  }

  getPrice() {
    let price = this.oriPrice
    this.promotions.forEach(p => {
      price = p(price)
    });
    return price
  }
}
class Book extends Goods {
  constructor(name, oriPrice, promotions) {
    super("图书", name, oriPrice, promotions)
  }
}
class Mobile extends Goods {
  constructor(name, oriPrice, promotions) {
    super("手机", name, oriPrice, promotions)
  }
}
class Clothing extends Goods {
  constructor(name, oriPrice, promotions) {
    super("服装", name, oriPrice, promotions)
  }
}

// 打折策略
function onSaleStrategy(price) {
  return price * 0.8
}

// 满100返50策略
function returnCashStrategy(price) {
  return price >= 100 ? (price - 50) : price
}

// 每满100返现50策略
function returnCash2Strategy(price) {
  return price - parseInt(price / 100) * 50
}

// 获得商品促销后总价
function getTotalPrice(goodsArr) {
  let totalPrice = 0
  goodsArr.forEach(g => {
    totalPrice += g.getPrice()
  })
  return totalPrice
}

const totalPrice = getTotalPrice([
  new Book("西游记", 13, [onSaleStrategy]),
  new Book("小米6", 1500, [onSaleStrategy, returnCashStrategy]),
  new Clothing("裙子", 500, [onSaleStrategy, returnCash2Strategy])
])


// 组合模式 -------------

class Company {
  constructor(name) {
    this.name = name
    this.children = []
  }

  add(company) {
    this.children.push(company)
  }

  remove(company) {
    this.children.splice(this.children.indexOf(company), 1)
  }

  display(level) {
    console.log('-'.repeat(level * 2) + this.name)
  }

  lineOfDuty() {
    console.log(this.name)
  }
}

class HRDepartment extends Company {
  add(company) {
    // 叶节点没有子节点
  }

  remove(company) {
    // 叶节点没有子节点
  }

  lineOfDuty() {
    console.log(this.name + "：员工招聘培训管理")
  }
}
class FinanceDepartment extends Company {
  add(company) {
    // 叶节点没有子节点
  }

  remove(company) {
    // 叶节点没有子节点
  }

  lineOfDuty() {
    console.log(this.name + "：公司财务收支管理")
  }
}

class ConcreteCompany extends Company {
  display(level) {
    super.display(level)
    this.children.forEach(c => {
      c.display(level + 1)
    })
  }

  lineOfDuty() {
    this.children.forEach(c => {
      c.lineOfDuty()
    })
  }
}

const root = new ConcreteCompany("北京总公司");
root.add(new HRDepartment("总公司人力资源部"));
root.add(new FinanceDepartment("总公司财务部"));

const shCorp = new ConcreteCompany("上海华东分公司");
shCorp.add(new HRDepartment("上海华东分公司人力资源部"));
shCorp.add(new FinanceDepartment("上海华东分公司财务部"));
root.add(shCorp);

const njCorp = new ConcreteCompany("南京办事处");
njCorp.add(new HRDepartment("南京办事处人力资源部"));
njCorp.add(new FinanceDepartment("南京办事处财务部"));
root.add(njCorp);

console.log("结构图：");
root.display(1);
console.log("职责：");
root.lineOfDuty();

// 享元模式 -------------

class Uploader {
  constructor(type, filePath) {
    this.type = type
    this.state = filePath ? "选择文件:" + filePath : "未选择文件"
    this.filePath = filePath
    console.log(this.state)
  }

  selectFile(filePath) {
    this.filePath = filePath
    this.state = "选择文件:" + filePath
    console.log(this.state)
  }

  upload() {
    this.state = "上传中:" + this.filePath
    console.log(this.state)
    return new Promise((resolve) => {
      setTimeout(() => {
        this.state = "上传完成:" + this.filePath
        console.log(this.state)
        resolve()
      }, 1000)
    })
  }
}

class FlashUploader extends Uploader {
  constructor(filePath) {
    super("Flash", filePath)
    this.flashParams = []
  }
}

class HtmlUploader extends Uploader {
  constructor(filePath) {
    super("Html", filePath)
    this.htmlParams = []
  }
}

// 未采用享元模式的代码
const uploaders = [
  new FlashUploader("文件1"),
  new FlashUploader("文件2"),
  new FlashUploader("文件3"),
  new HtmlUploader("文件4"),
  new HtmlUploader("文件5"),
  new HtmlUploader("文件6")
]

function upload(uploaders) {
  let uploader = uploaders.pop()
  uploader.upload().then(() => {
    if (uploaders.length) {
      upload(uploaders)
    }
  })
}
upload(uploaders)

// 采用享元模式
const uploaderFactory = (function () {
  let uploaders = []
  return function uploaderFactory(type, filePath) {
    let uploader = uploaders.find(u => u.type === type)
    if (!uploader) {
      uploader = type === "Html" ? new HtmlUploader() : new FlashUploader()
      uploaders.push(uploader)
    }
    uploader.selectFile(filePath)
    console.log(uploaders, '=================')
    return uploader
  }
})()
uploaderFactory('Flash', "文件1").upload()
  .then(() => uploaderFactory('Flash', "文件2"))
  .then(() => uploaderFactory('Flash', "文件3"))
  .then(() => uploaderFactory('Flash', "文件4"))
  .then(() => uploaderFactory('Html', "文件5"))
  .then(() => uploaderFactory('Html', "文件6"))


// 命令模式 -------------
// 示例1
class Command {
  execute() {
    // 由子类实现
  }
}
class DeleteCommand extends Command {
  execute() {
    console.log("删除")
  }
}
class AddCommand extends Command {
  execute() {
    console.log("添加")
  }
}
class EditCommand extends Command {
  execute() {
    console.log("编辑")
  }
}
class MenuItem {
  constructor(text, command) {
    this.text = text
    this.command = command
  }
  click() {
    this.command.execute()
  }
}

var deleteMenu = new MenuItem("删除", new DeleteCommand())
var addMenu = new MenuItem("新增", new AddCommand())
var editMenu = new MenuItem("编辑", new EditCommand())
deleteMenu.click()


// 示例2
let pos = {
  x: 0,
  y: 0
}
class Command {
  constructor(commandName) {
    this.commandName = commandName
  }
  execute() {
    // 由子类实现
  }
  canExecute(commandName) {
    return this.commandName == commandName
  }
}
class UpCommand extends Command {
  constructor() {
    super('up')
  }
  execute() {
    pos.y -= 1
    console.log("上", pos.x + ' | ' + pos.y)
  }

}
class DownCommand extends Command {
  constructor() {
    super('down')
  }
  execute() {
    pos.y += 1
    console.log("下", pos.x + ' | ' + pos.y)
  }
}
class LeftCommand extends Command {
  constructor() {
    super('left')
  }
  execute() {
    pos.x -= 1
    console.log("左", pos.x + ' | ' + pos.y)
  }
}
class RightCommand extends Command {
  constructor() {
    super('right')
  }
  execute() {
    pos.x += 1
    console.log("右", pos.x + ' | ' + pos.y)
  }
}
const commandManager = (function () {
  let commands = [];

  function addCommand(command) {
    commands.push(command)
  }

  function invoke(commandName) {
    commands.forEach(m => {
      if (m.canExecute(commandName)) {
        m.execute()
      }
    })
  }
  return {
    addCommand,
    invoke
  }
})()
commandManager.addCommand(new UpCommand())
commandManager.addCommand(new DownCommand())
commandManager.addCommand(new LeftCommand())
commandManager.addCommand(new RightCommand())
commandManager.invoke('up')

// 示例三，redux也采用了命令模式思想
class Reducer {
  constructor(action) {
    this.action = action
  }
  execute() {
    // 由子类实现
  }
  canExecute(action) {
    return this.action == action
  }
}
class DeleteReducer extends Reducer {
  constructor() {
    super('delete')
  }
  execute() {
    console.log("删除")
  }

}
class AddReducer extends Reducer {
  constructor() {
    super('add')
  }
  execute() {
    console.log("添加")
  }
}
class EditReducer extends Reducer {
  constructor() {
    super('edit')
  }
  execute() {
    console.log("编辑")
  }
}

const store = (function () {
  let reducers = [];

  function addReducer(reducer) {
    reducers.push(reducer)
  }

  function dispatch(action) {
    reducers.forEach(m => {
      if (m.canExecute(action)) {
        m.execute()
      }
    })
  }
  return {
    addReducer,
    dispatch
  }
})()
store.addReducer(new DeleteReducer())
store.addReducer(new AddReducer())
store.addReducer(new EditReducer())
store.dispatch('add')


// 中介者模式 -------------
var unitedNations = {
  colleagueUSA: null,
  colleagueIraq: null,
  declare(message, colleague) {
    if (colleague.name == "美国") {
      this.colleagueIraq.getMessage(message);
    } else if (colleague.name == "伊拉克") {
      this.colleagueUSA.getMessage(message);
    }
  }
}

class Country {
  constructor(name) {
    this.name = name
    this.mediator = unitedNations
  }
  declare(message) {
    console.log(this.name + "发出声明:" + message)
    this.mediator.declare(message, this)
  }
  getMessage(message) {
    console.log(this.name + "获知声明:" + message)
  }
}

class USA extends Country {
  constructor() {
    super("美国")
  }
}

class Iraq extends Country {
  constructor() {
    super("伊拉克")
  }
}

const usa = new USA()
const iraq = new Iraq()
unitedNations.colleagueUSA = usa
unitedNations.colleagueIraq = iraq
usa.declare("不准研制核武器，否则发动战争")
iraq.declare("我们没有研制核武器")

// 迭代器模式 -------------
class Iterator {
  constructor(aggregate) {
    this.aggregate = aggregate
    this.current = 0
  }
  first() {
    return this.aggregate[0]
  }
  next() {
    let ret = null;
    this.current++
      if (this.current < this.aggregate.length) {
        ret = this.aggregate[this.current]
      }
    return ret
  }
  isDone() {
    return this.current >= this.aggregate.length
  }
  currentItem() {
    return this.aggregate[this.current]
  }
}

var iterator = new Iterator(['e1', 'e2', 'e3', 'e4', 'e5', 'e6'])
while (!iterator.isDone()) {
  console.log(iterator.currentItem());
  iterator.next()
}


// 职责链模式 -------------
class Manager {
  constructor(superior) {
    this.superior = superior
  }

  setSuperior(superior) {
    this.superior = superior
  }

  handleRequest(levelRequest) {

  }
}

class CommonManager extends Manager {
  handleRequest(levelRequest) {
    if (levelRequest > 2) {
      this.superior.handleRequest(levelRequest);
    } else {
      console.log(`请假${levelRequest}天被经理审批通过`);
    }
  }
}

class Majordomo extends Manager {
  handleRequest(levelRequest) {
    if (levelRequest > 5) {
      this.superior.handleRequest(levelRequest);
    } else {
      console.log(`请假${levelRequest}天被总监审批通过`);
    }
  }
}

class GeneralManager extends Manager {
  handleRequest(levelRequest) {
    console.log(`请假${levelRequest}天被总经理审批通过`);
  }
}

const manager = new CommonManager();
const superior1 = new Majordomo();
manager.setSuperior(superior1);
const superior2 = new GeneralManager();
superior1.setSuperior(superior2);

console.log("请假2天：");
manager.handleRequest(2);
console.log("请假4天：");
manager.handleRequest(4);
console.log("请假6天：");
manager.handleRequest(6);

// 备忘录模式 -------------
class GameRole {
  constructor(atk, vit, def) {
    // 生命力
    this.atk = atk;
    // 攻击力
    this.vit = vit;
    // 防御力
    this.def = def;
  }

  recoveryState(momento) {
    this.atk = momento.atk;
    this.vit = momento.vit;
    this.def = momento.def;
  }

  saveState() {
    return new RoleStateMomento(this.vit, this.atk, this.def);
  }
}

class RoleStateMomento {
  constructor(vit, atk, def) {
    this.vit = vit
    this.atk = atk
    this.def = def
  }
}

const mementoStore = (function () {
  let items = []
  return {
    getMemento(timestamp) {
      return items.find(i => i.timestamp = timestamp).momento
    },
    setMemento(timestamp, momento) {
      items.push({
        timestamp,
        momento
      })
    },
    deleteMemento(timestamp) {
      items.splice(items.findIndex(i => i.timestamp == timestamp), 1)
    }
  }
})()

var role = new GameRole(1, 2, 3)
const timestamp = new Date()
mementoStore.setMemento(timestamp, role.saveState())
role.atk = 120
console.log(role)
role.recoveryState(mementoStore.getMemento(timestamp))
console.log(role)

// 状态模式 -------------
class State {
  writeProgram(w) {

  }
}
class ForenoonState extends State {
  writeProgram(w) {
    if (w.hour < 12) {
      console.log(`当前时间：${w.hour}点，上午工作，精神百倍。`);
    } else {
      w.setState(new NoonState());
      w.writeProgram();
    }
  }
}
class NoonState extends State {
  writeProgram(w) {
    if (w.hour < 14) {
      console.log(`当前时间：${w.hour}点，中午工作，犯困。`);
    } else {
      w.setState(new AfternoonState());
      w.writeProgram();
    }
  }
}
class AfternoonState extends State {
  writeProgram(w) {
    if (w.hour < 18) {
      console.log(`当前时间：${w.hour}点，下午工作，状态不错。`);
    } else {
      w.setState(new ResetState());
      w.writeProgram();
    }
  }
}
class ResetState extends State {
  writeProgram(w) {
    w.isFinished = true;
    console.log(`当前时间：${w.hour}点，下班了，结束工作。`);
  }
}

class Work {
  constructor() {
    this.currentState = new ForenoonState()
    this.isFinished = false
    this.hour = 0
  }

  setState(state) {
    this.currentState = state;
  }

  writeProgram() {
    this.currentState.writeProgram(this);
  }
}

let w = new Work();
w.hour = 9;
w.writeProgram();
w.hour = 13;
w.writeProgram();
w.hour = 17;
w.writeProgram();
w.hour = 19;
w.writeProgram();

// 访问者模式 -------------
// 例子1
const healthCheckItemList = (function () {
  var checkItems = []
  return {
    attach(checkItem) {
      checkItems.push(checkItem)
    },
    detach(checkItem) {
      checkItems.splice(checkItems.indexOf(checkItem), 1)
    },
    accept(examiner) {
      checkItems.forEach(c => {
        c.accept(examiner)
      })
    }
  }
})()

// 体检项抽象类
class HealthCheck {
  constructor(name) {
    this.name = name
  }
  accept(examiner) {

  }
}

// 体检项：检查视力
class SightTest extends HealthCheck {
  constructor() {
    super("视力")
  }
  accept(examiner) {
    examiner.getSightResult(this);
  }
}

// 体检项：检查口腔
class MouthExam extends HealthCheck {
  constructor() {
    super("口腔")
  }
  accept(examiner) {
    examiner.getMouthResult(this);
  }
}

// 体检者抽象类
class Examiner {
  constructor(corp, name) {
    this.corp = corp
    this.name = name
  }
  getSightResult(sightTest) {
    console.log(`${this.corp}员工${this.name}检查${sightTest.name}`)
  }

  getMouthResult(mouthExam) {
    console.log(`${this.corp}员工${this.name}检查${mouthExam.name}`)
  }
}

// 蓝海员工
class LanHai extends Examiner {
  constructor(name) {
    super("蓝海", name)
  }
  getSightResult(sightTest) {
    super.getSightResult(sightTest)
    console.log("特殊福利，赠送眼药水一瓶")
  }

  getMouthResult(mouthExam) {
    super.getMouthResult(mouthExam)
    console.log("特殊福利，赠送牙齿美白一次")
  }
}

// 易联众员工
class YiLianZhong extends Examiner {
  constructor(name) {
    super("易联众", name)
  }
}

healthCheckItemList.attach(new SightTest())
healthCheckItemList.attach(new MouthExam())
healthCheckItemList.accept(new LanHai("张三"))
healthCheckItemList.accept(new LanHai("李四"))
healthCheckItemList.accept(new YiLianZhong("王五"))